#include <Thunk/CdeclToCdecl.h>

#define THUNK_MACHINE_CODE_IMPLEMENT
#include <Thunk/MachineCodeMacro.h>

namespace Thunk
{
	void __declspec( naked ) CdeclToCdecl::Hook()
	{
		_asm
		{
			POP EAX
			// p=&m_method; &m_this=p+4; &oldESP=p+9; &oldRet=p+14
			
			// Save Caller Return
			POP ECX //dst
			// offset=dst-src(JMP)-5=dst-( EAX+13 )-5
			SUB ECX,18
			SUB ECX,EAX
			MOV DWORD PTR[EAX+14],ECX

			// Save oldESP
			MOV DWORD PTR[EAX+9],ESP

			// PUSH this
			PUSH DWORD PTR[EAX+4]
			
			// Set Callee Return
			MOV ECX,EAX
			ADD ECX,8
			PUSH ECX

			// JMP method
			//MOV EAX,DWORD PTR[EAX]
			JMP DWORD PTR[EAX]
		}
	}

	dword_ptr CdeclToCdecl::GetObject() const
	{
		return m_this;
	}

	void CdeclToCdecl::SetObject(dword_ptr newObj)
	{
		m_this = newObj;
		Helper::FlushInstructionCache(&m_this,sizeof(m_this));

	}

	dword CdeclToCdecl::GetMethod() const
	{
		return m_method;
	}

	void CdeclToCdecl::SetMethod(dword newMethod)
	{
		m_method = newMethod;
		Helper::FlushInstructionCache(&m_method,sizeof(m_method));
	}

	CdeclToCdecl::~CdeclToCdecl()
	{}

	CdeclToCdecl::CdeclToCdecl(dword_ptr Obj,dword method)
		CDECL_TO_CDECL_CODES()
	{
		Helper::SetTransferDST(&CALL,Helper::PointerToInt32(Hook));
		SetObject(Obj);
		SetMethod(method);
	}

	CdeclToCdecl::CdeclToCdecl(const CdeclToCdecl &src)
		CDECL_TO_CDECL_CODES()
	{
		Helper::SetTransferDST(&CALL,Helper::PointerToInt32(Hook));
		*this = src;
	}

	CdeclToCdecl& CdeclToCdecl::operator = (const CdeclToCdecl &rhs)
	{
		SetObject( rhs.GetObject() );
		SetMethod( rhs.GetMethod() );
		return *this;
	}

	bool CdeclToCdecl::operator == (const CdeclToCdecl &rhs) const
	{
		return (GetObject()==rhs.GetObject() && GetMethod()==rhs.GetMethod());
	}

	bool CdeclToCdecl::operator != (const CdeclToCdecl &rhs) const
	{
		return !(*this==rhs);
	}

	dword_ptr CdeclToCdecl::Attach(dword_ptr newObj)
	{
		dword_ptr oldObj = GetObject();
		SetObject(newObj);
		return oldObj;
	}

	dword CdeclToCdecl::Attach(dword newMethod)
	{
		dword oldMethod = GetMethod();
		SetMethod(newMethod);
		return oldMethod;
	}
}
